/**
 * 
 */
package com.ms.game.itembox.db;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.type.StandardBasicTypes;

import redis.clients.jedis.Jedis;

import com.google.gson.Gson;
import com.ms.game.itembox.AccountItemChangeWay;
import com.ms.game.itembox.ChangeItem;
import com.ms.game.itembox.ChangeItemLister;
import com.ms.game.itembox.Item;
import com.ms.game.itembox.ItemChangeParam;
import com.ms.game.itembox.ItemChangeWay;
import com.ms.game.itembox.ItemStatus;
import com.ms.game.itembox.container.ItemBasic;
import com.ms.game.itembox.container.ItemBoxBin;
import com.ms.game.itembox.container.ItemExtraSettings;
import com.ms.game.itembox.container.ItemTreasureBox;
import com.ms.player.Player;
import com.ms.player.PlayerAttributeType;
import com.ms.player.PlayerUserType;
import com.ms.player.UpdateAttributeList;
import com.ms.service.db.jedis.JedisManager;
import com.ms.service.error.ErrorCode;
import com.ms.service.module.analysisgroup.AnalysisGroupProcessor;
import com.xpec.c4.db.dao.PlayerDAO;
import com.xpec.c4.db.hibernate.manager.HibernateLogManager;
import com.xpec.c4.db.hibernate.manager.HibernateManager;
import com.xpec.c4.db.model.basic.BasicGameModel;
import com.xpec.c4.db.model.log.basic.BasicLogModel;
import com.xpec.c4.game.time.TimeConstants;
import com.xpec.c4.game.time.TimeSys;
import com.xpec.c4.game.utility.GUID;
import com.xpec.c4.game.utility.GUIDFactory;
import com.xpec.c4.game.utility.GUIDType;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ModuleName;
import com.xpec.c4.service.module.common.ItemInfoManager;

/**
 * @author TW15010
 *
 */
public class ItemDAO {
	
	private static Log log = LogFactory.getLog(ItemDAO.class);

	public static void saveGameModel(BasicGameModel basicGameModel){
		Session session = HibernateManager.txBegin();
		session.save(basicGameModel);
		HibernateManager.txCommitFailRollbackClearSession();
	}
	
	public static void saveLogModel(BasicLogModel basicLogModel) {
		Session session = HibernateLogManager.txBegin();
		session.save(basicLogModel);
		HibernateLogManager.txCommitFailRollbackClearSession();
	}
	
	/**
	 * 建立一個道具的資料庫 model, 只是記憶體中建立資料, 不含儲存
	 * 
	 * @param ownerId
	 *            擁有者 guid
	 * @param itemId
	 *            道具 id, 參考 {@link ItemBoxBin}
	 * @param count
	 *            堆疊數量
	 * @param refineLevel 
	 * 			    精煉等級
	 * @return
	 */
	public static PlayerItemModel createItemModel
	(
			ItemChangeWay changeWay,
			String ownerId,
			String itemId,
			int count, 
			int refineLevel,
			int playerLevel,
			int lastLevelBoxUseLevel,
			String createGameType,
			String createHallType
	)
	{
		PlayerItemModel model = new PlayerItemModel();

		ItemBasic itemBin = ItemInfoManager.getInstance().getItem(itemId);

		model.setSn(GUIDFactory.createUUID(GUIDType.TYPE_ITEM).toString());
		model.setOwnerId(ownerId);
		
		//新增的道具ID
		model.setItemId(itemId);
		
		//填上預設的itemId
		model.setBasicItemId(itemBin.getDefaultItemId());

		if(refineLevel == 0){
			//如果精煉等級是0 ， 再檢查一次bin檔內容是多少
			refineLevel = itemBin.getItemRefineLevel();
		}
		
		model.setRefineLevel(refineLevel);
		
		// 精煉到期時間
		long refineEndTime = 0;
		// 道具過期時間
		long expireTime = 0;
		if(changeWay != ItemChangeWay.ADD_SEND_GIFT) {
			if (itemBin.getRefineExpireTime() > 0) {
				//如果可以精煉，就把精煉時間加上去
				Date refineEndDate = new Date(TimeSys.currentTimeMills() + (itemBin.getRefineExpireTime() * TimeConstants.MILLI_SECOND_PRE_HOUR));
				refineEndDate = GameUtility.getDateOnly(refineEndDate);
				refineEndTime = refineEndDate.getTime();
			} 
			if (itemBin.isHasExpireTime()) {
				expireTime = TimeSys.currentTimeMills() + (itemBin.getExpireTime() * TimeConstants.MILLI_SECOND_PRE_HOUR);
				Date date = new Date(expireTime);
				date = GameUtility.getDateOnly(date);
				if(date.equals( GameUtility.getDateOnly(new Date()))){
					date = DateUtils.addDays(date, 1);
				}
				expireTime = date.getTime() / TimeConstants.MILLI_SECOND_PRE_SECOND;
				model.setExpireTime(TimeSys.currentTimeSec() + itemBin.getExpireTime() * TimeConstants.SECOND_PRE_HOUR);
			}
		
			if(itemBin.isTreasureBox()){
				ItemTreasureBox treasureBox = (ItemTreasureBox)itemBin;
				if (treasureBox.getTreasureBoxType() == ItemTreasureBox.TYPE_RECYCLE) { //循環寶箱
					ItemExtraSettings extraSettings = new ItemExtraSettings();
					int level = (lastLevelBoxUseLevel + treasureBox.getUseLevel()) / treasureBox.getUseLevel() * treasureBox.getUseLevel();
					extraSettings.setUseLevel(level);
					model.setExtraSettings(extraSettings);
				}
			}
			
		} else {
			//如果是因為虛寶交易獲得物品，並且原本就有精煉過期時間的道具，就設成當下時間，讓他過期
			if(itemBin.getRefineExpireTime() > 0){
				refineEndTime = TimeSys.currentTimeMills();
			}
		}
		
		model.setRefineEndTime(refineEndTime);
		model.setCount(count);
		model.setStatus(ItemStatus.NORMAL.getId());
		
		//建立model的來源遊戲與館別
		model.setCreateItemWay(changeWay);
		model.setCreateGameType(createGameType);
		model.setCreateHallType(createHallType);
		
		return model;
	}
	
	public static PlayerItemChangeLogModel createPlayerItemChangeLogModel(String orderId, ItemChangeWay changeWay, String sn, 
			Player player, String itemId, int changeCount, int changeBefore, int changeAfter, 
			String hallId, String hallName, int level, String extraSettings) {
		PlayerItemChangeLogModel logModel = new PlayerItemChangeLogModel();
		logModel.setAccountId(player.getAccountId().toString());
		logModel.setPlayerName(player.getNameWithNumber());
		logModel.setChangeCount(changeCount);
		logModel.setBeforeChange(changeBefore);
		logModel.setAfterChange(changeAfter);
		logModel.setPlayerId(player.getGuid().toString());
		logModel.setItemId(itemId);
		logModel.setChangeWay(changeWay.getId());
		logModel.setChangeWayDesc(changeWay.name());
		logModel.setGameHallId(hallId);
		logModel.setGameHallName(hallName);
		logModel.setItemSn(sn);
		ItemBasic itemBasic = ItemInfoManager.getInstance().getItem(itemId);
		if(itemBasic != null){
			logModel.setItemName(itemBasic.getName());
		}
		logModel.setUserType(player.getUserType());
		logModel.setOrderId(orderId);
		logModel.setRefineLevel(level);
		logModel.setExtraSettings(extraSettings);
		return logModel;
	}

	/**
	 * 載入玩家擁有的所有道具
	 * 
	 * @param playerId
	 * @return 道具列表<br>
	 *         key: 道具序號<br>
	 *         value: 道具資料
	 */
	public static HashMap<String, PlayerItemModel> loadPlayerItems(GUID playerId) {
		if (playerId == null) {
			return null;
		}

		// 載入所有屬於指定角色的道具資料
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("from PlayerItemModel where ownerId = :ownerId and status = :status");
		query.setParameter("ownerId", playerId.toString());
		query.setParameter("status", ItemStatus.NORMAL.getId());
		@SuppressWarnings("unchecked")
		ArrayList<PlayerItemModel> list = (ArrayList<PlayerItemModel>) query.list();
		HibernateManager.endSession();

		// 將道具列表轉成 map 回傳
		HashMap<String, PlayerItemModel> itemModelMap = new HashMap<String, PlayerItemModel>();
		for (PlayerItemModel model : list) {
			itemModelMap.put(model.getSn(), model);
		}

		return itemModelMap;
	}

	/**
	 * 將道具資料載入並放到玩家身上
	 * 
	 * @param player
	 *            角色資料
	 * @param currentModule
	 *            目前所在的 module 實體
	 */
	public static void LoadItemDataToPlayer(Player player, BasicServiceModule currentModule) {
		if (player == null) {
			return;
		}

		if (currentModule == null) {
			return;
		}

		// 載入角色的道具資料
		HashMap<String, PlayerItemModel> itemModelMap = loadPlayerItems(player.getGuid());

		// 檢查道具資料, 排除有問題的
		verifyInvalidItems(player, currentModule, itemModelMap);

		// 將道具資料放到角色中
		player.getItemBox().getItems().clear();
		for (Map.Entry<String, PlayerItemModel> entry : itemModelMap.entrySet()) {
			Item item = new Item(entry.getValue());
			player.getItemBox().getItems().add(item);
		}
	}

	/**
	 * 驗證道具資料，將過期的，有問題的移除並寫 log
	 * 
	 * @param player
	 *            角色資料
	 * @param currentModule
	 *            目前所在的 module 實體
	 * @param playerItems
	 *            角色的道具列表, 來自 {@link #loadPlayerItems(GUID)}
	 */
	public static void verifyInvalidItems(Player player, BasicServiceModule currentModule,
			HashMap<String, PlayerItemModel> playerItems) {
		if (player == null) {
			return;
		}

		if (currentModule == null) {
			return;
		}

		if (playerItems == null) {
			return;
		}

		// 放有問題資料的清單
		HashMap<String, PlayerItemModel> invalidItems = new HashMap<String, PlayerItemModel>();

		// 掃一遍所有的道具資料
		Iterator<Entry<String, PlayerItemModel>> it = playerItems.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry<String, PlayerItemModel> mapEntry = (Entry<String, PlayerItemModel>) it.next();
			// 道具資料
			PlayerItemModel itemModel = mapEntry.getValue();

			// 是否存在於 bin 檔中 ，並且是水晶卡跟福袋==================
			ItemBasic itemBin = ItemInfoManager.getInstance().getItem(itemModel.getItemId());
			if (itemBin == null || itemBin.isCrystalCard()) {
				itemModel.setStatus(ItemStatus.ITEM_ID_NOT_EXIST.getId());
				invalidItems.put(itemModel.getSn(), itemModel);
				it.remove();
				continue;
			}

			// 是否超過可精煉時間 =====================
			// 可以精煉的道具 && 還沒精煉過
			if (ItemInfoManager.getInstance().isItemCanRefine(itemBin) && itemModel.getRefineLevel() == 0) {
				// 超過可精煉時間
				if (ItemInfoManager.getInstance().isRefineTimeExpire(itemModel.getRefineEndTime(), TimeSys.currentTimeMills())) {
					itemModel.setStatus(ItemStatus.REFINE_TIME_EXPIRED.getId());
					invalidItems.put(itemModel.getSn(), itemModel);
					it.remove();
					continue;
				}
			}
			
			if(itemModel.getExpireTime() > 0){ //有過期時間
				if(TimeSys.currentTimeSec() > itemModel.getExpireTime()){
					itemModel.setStatus(ItemStatus.TIME_EXPIRE.getId());
					invalidItems.put(itemModel.getSn(), itemModel);
					it.remove();
					continue;
				}
			}
		} 

		// 所有道具都正常，直接結束
		if (invalidItems.size() == 0) {
			return;
		}

		// 更新回 db
		Session session = HibernateManager.txBegin();

		for (Map.Entry<String, PlayerItemModel> entry : invalidItems.entrySet()) {
			// 道具資料
			PlayerItemModel itemModel = entry.getValue();
			ItemBasic itemBin = ItemInfoManager.getInstance().getItem(itemModel.getItemId());

			// 更新
			session.saveOrUpdate(itemModel);

			// 產生 log
			PlayerItemChangeLogModel log = new PlayerItemChangeLogModel();

			log.setOrderId(GUIDFactory.createUUID(GUIDType.TYPE_ITEM).toString());
			log.setAccountId(player.getAccountId().toString());
			log.setPlayerId(player.getGuid().toString());
			log.setPlayerName(player.getName());
			log.setUserType(player.getUserType());
			log.setItemSn(itemModel.getSn());
			log.setItemId(itemModel.getItemId());
			log.setRefineLevel(itemModel.getRefineLevel());
			log.setExtraSettings(new Gson().toJson(itemModel.getExtraSettings()));

			if (itemBin != null) {
				log.setItemName(itemBin.getName());
			}

			log.setChangeCount(itemModel.getCount());
			log.setBeforeChange(itemModel.getCount());
			log.setAfterChange(0);

			ItemStatus itemStatus = ItemStatus.getItemStatusbyId(itemModel.getStatus());
			ItemChangeWay changeWay = null;
			switch (itemStatus) {
			case ITEM_ID_NOT_EXIST:
				changeWay = ItemChangeWay.SUB_INVALID_ITEM_ID;
				break;
			case REFINE_TIME_EXPIRED:
				changeWay = ItemChangeWay.SUB_ITEM_REFINE_TIME_EXPIRE;
				break;
			case TIME_EXPIRE:
				changeWay = ItemChangeWay.SUB_TIME_EXPIRE;
				break;
			}
		
			if (changeWay != null) {
				log.setChangeWay(changeWay.getId());
				log.setChangeWayDesc(changeWay.getDesc());
			}
			

			currentModule.saveDbLog(log);
			
			//給統計
			if(changeWay != null && player.getUserType() == PlayerUserType.PLAYER){
				if(changeWay == ItemChangeWay.SUB_ITEM_REFINE_TIME_EXPIRE){
					AnalysisGroupProcessor.sendItemChangeToAnalysis(changeWay, itemModel.getItemId(), itemModel.getRefineLevel(), itemModel.getCount());
				}
				
				if(changeWay == ItemChangeWay.SUB_INVALID_ITEM_ID){
					AnalysisGroupProcessor.sendInvalidItemChangeToAnalysis(false, itemModel.getItemId(), itemModel.getRefineLevel(), itemModel.getCount());
				}
			}
		}

		HibernateManager.txCommitFailRollbackClearSession();
	}

	/**
	 * 載入指定寄送禮物訂單 id 的所有道具
	 * 
	 * @param sendGiftOrderId
	 *            寄送禮物訂單 orded id
	 * @return
	 */
	public static ArrayList<PlayerItemModel> loadSendGiftItems(String sendGiftOrderId) {
		if (sendGiftOrderId == null) {
			return null;
		}

		// 載入所有屬於指定寄送禮物 order id 的道具資料
		Session session = HibernateManager.getSession();
		Query query = session.createQuery("from PlayerItemModel where sendGiftOrderId = :sendGiftOrderId");
		query.setParameter("sendGiftOrderId", sendGiftOrderId);
		@SuppressWarnings("unchecked")
		ArrayList<PlayerItemModel> list = (ArrayList<PlayerItemModel>) query.list();
		HibernateManager.endSession();

		return list;
	}

	/**
	 * 載入指定寄送禮物訂單 id 的所有道具, 用 map 形式輸出
	 * 
	 * @param sendGiftOrderId
	 *            寄送禮物訂單 orded id
	 * @return
	 */
	public static HashMap<String, PlayerItemModel> loadSendGiftItemsToMap(String sendGiftOrderId) {

		// 載入指定的寄送禮物資料
		ArrayList<PlayerItemModel> sendGiftItemList = ItemDAO.loadSendGiftItems(sendGiftOrderId);

		// 轉成 map
		HashMap<String, PlayerItemModel> sendGiftItemMap = new HashMap<String, PlayerItemModel>();
		for (PlayerItemModel giftItemModel : sendGiftItemList) {
			sendGiftItemMap.put(giftItemModel.getSn(), giftItemModel);
		}

		return sendGiftItemMap;
	}

	/**
	 * 扣道具，檢查相關條件是否正確
	 * <p>
	 * 可以同時異動好幾個道具，只要有一個道具發生錯誤就不會繼續往下處理。
	 * 
	 * @param param
	 *            相關條件參數，
	 *            執行結果會放在 {@link ItemChangeParam#getResult()}，如果取得的值 =
	 *            {@link ErrorCode#CHANGE_ITEM_ITEM_ERROR} 就要看各個道具裡面的 error
	 *            code {@link ChangeItem#getResult()} 才能知道正確的原因。
	 * @return 道具列表<br>
	 *         key: 道具序號<br>
	 *         value: 道具資料
	 */
	public static ItemChangeProcessData changeItemVerify(ItemChangeParam param) {
		// 沒有給執行參數
		if (param == null) {
			return null;
		}

		// 沒有交易id
		if (param.getOrderId() == null) {
			param.setResult(ErrorCode.NO_ORDER_ID);
			return null;
		}

		// 道具異動清單沒資料
		ChangeItemLister itemLister = param.getChangeItemLister();
		if (itemLister == null || itemLister.getSize() == 0) {
			param.setResult(ErrorCode.CHANGE_ITEM_NO_ITEM_LISTER);
			return null;
		}

		// 道具異動過程中需要的暫存資料
		ItemChangeProcessData itemChangeProcData = new ItemChangeProcessData();

		// 載入角色所有道具
		HashMap<String, PlayerItemModel> itemModelMap = loadPlayerItems(param.getPlayerId());
		itemChangeProcData.setPlayerItemMap(itemModelMap);
		
		int crystalCountDB = 0;
		if (param.isAddCrystal()) { //如果有水晶，要把角色身上的水晶讀出來
			crystalCountDB = PlayerDAO.loadPlayerCrystalCount(param.getPlayerId().toString());
		}

		// 載入寄送禮物訂單內所有道具
		HashMap<String, PlayerItemModel> sendGiftItemMap = null;
		if (param.getChangeWay().isSendGiftRecvItem()) {
			// 跑一遍所有的寄送禮物訂單
			sendGiftItemMap = new HashMap<String, PlayerItemModel>();
			for (String sendGiftOrderId : itemLister.getSendGiftOrderIdList()) {
				// 載入訂單中的物品
				ArrayList<PlayerItemModel> tmpItemList = loadSendGiftItems(sendGiftOrderId);
				// 加到 map 中
				for (PlayerItemModel sendItemModel : tmpItemList) {
					sendGiftItemMap.put(sendItemModel.getSn(), sendItemModel);
				}
			}
			itemChangeProcData.setSendGiftItemMap(sendGiftItemMap);
		}
		
		// 掃一遍所有要變更的道具
		Iterator<Map.Entry<String, ChangeItem>> it = itemLister.getListIterator();
		ChangeItem itemInfo = itemLister.getNextItem(it);
		while (itemInfo != null) {
			// 道具id檢查
			ItemBasic itemBasic = ItemInfoManager.getInstance().getItem(itemInfo.getItemId());
			if (itemBasic == null) {
				itemInfo.setResult(ErrorCode.CHANGE_ITEM_INVALID_ITEM_ID);
				param.setResult(ErrorCode.CHANGE_ITEM_ITEM_ERROR);
				break;
			}

			// 角色身上的數量
			int currentCount = itemInfo.getCurrentCount();

			if (itemBasic.isCrystalCard()) {// 如果這個道具是水晶的話
				if(currentCount != crystalCountDB){
					itemInfo.setResult(ErrorCode.CHANGE_ITEM_CRYSTAL_COUNT_DIFFERENT_WITH_DB);
					param.setResult(ErrorCode.CHANGE_ITEM_ITEM_ERROR);
					break;
				}else {					
					// 處理下一個
					itemInfo.setResult(ErrorCode.SUCCESS);
					itemInfo = itemLister.getNextItem(it);
					continue;
				}
			}

			// DB 中的數量
			int currentCountDB = 0;
			if (param.getChangeWay().isAdd()) {
				// 增加物品 用 id 去找
				for (Map.Entry<String, PlayerItemModel> entry : itemModelMap.entrySet()) {
					PlayerItemModel model = entry.getValue();
					if (model.getItemId().equals(itemInfo.getItemId())) {
						currentCountDB = model.getCount();
						break;
					}
				}
			} else {
				// 減少物品 用序號去找
				if (itemModelMap.containsKey(itemInfo.getItemSn())) {
					currentCountDB = itemModelMap.get(itemInfo.getItemSn()).getCount();
				}
			}

			// 角色身上的數量和 db 上的不一樣
			if (currentCount != currentCountDB) {
				itemInfo.setResult(ErrorCode.CHANGE_ITEM_COUNT_DIFFERENT_WITH_DB);
				param.setResult(ErrorCode.CHANGE_ITEM_ITEM_ERROR);
				break;
			}

			// 變更的數值不能是 0
			if (itemInfo.getChangeCount() == 0) {
				itemInfo.setResult(ErrorCode.CHANGE_ITEM_CHANGE_COUNT_CANT_BE_ZERO);
				param.setResult(ErrorCode.CHANGE_ITEM_ITEM_ERROR);
				break;
			}

			// 變更的數值不能是負的
			if (itemInfo.getChangeCount() < 0) {
				itemInfo.setResult(ErrorCode.CHANGE_ITEM_CHANGE_COUNT_CANT_BE_NEGATIVE);
				param.setResult(ErrorCode.CHANGE_ITEM_ITEM_ERROR);
				break;
			}

			// 變更後超過最大值檢查
			if (param.getChangeWay().isAdd()) {
				// 允許增加的最大量
				int diff = Integer.MAX_VALUE - currentCount;
				if (itemInfo.getChangeCount() > diff) {
					itemInfo.setResult(ErrorCode.CHANGE_ITEM_RESULT_MORE_THEN_MAX_COUNT);
					param.setResult(ErrorCode.CHANGE_ITEM_ITEM_ERROR);
					break;
				}
			}

			// 變更後低於 0
			if (param.getChangeWay().isSubtract()) {
				// 不夠扣
				if (currentCount < itemInfo.getChangeCount()) {
					itemInfo.setResult(ErrorCode.CHANGE_ITEM_CURRENT_COUNT_NOT_ENOUGH);
					param.setResult(ErrorCode.CHANGE_ITEM_ITEM_ERROR);
					break;
				}
			}

			// 如果是因為寄送禮物而獲得道具, 異動清單內的資料必須存在於寄送禮物的道具清單中
			if (param.getChangeWay().isSendGiftRecvItem()) {
				// 指定的道具序號不存在於寄送的道具清單中
				if (!sendGiftItemMap.containsKey(itemInfo.getItemSn())) {
					itemInfo.setResult(ErrorCode.CHANGE_ITEM_SN_IS_NOT_EXIST_IN_SEND_GIFT_LIST);
					param.setResult(ErrorCode.CHANGE_ITEM_ITEM_ERROR);
					break;
				}
			}

			// 處理下一個
			itemInfo.setResult(ErrorCode.SUCCESS);
			itemInfo = itemLister.getNextItem(it);
		}

		if (param.getResult() == ErrorCode.CHANGE_ITEM_ITEM_ERROR) {
			return null;
		}

		param.setResult(ErrorCode.SUCCESS);
		return itemChangeProcData;
	}

	/**
	 * 扣道具，將執行後的結果寫回 db
	 * 
	 * @param param
	 *            相關條件參數 {@link ItemChangeParam}<br>
	 *            執行結果會放在 {@link ItemChangeParam#getResult()}
	 * @param itemChangeProcData
	 *            道具異動過程中需要的暫存資料
	 */
	public static void changeItemApply(ItemChangeParam param, ItemChangeProcessData itemChangeProcData, UpdateAttributeList updateAttributeList) {
		// 沒有給執行參數
		if (param == null) {
			return;
		}

		// 道具異動過程中需要的暫存資料
		if (itemChangeProcData == null) {
			param.setResult(ErrorCode.INVALID_PARAM);
			return;
		}

		// 沒有玩家道具列表
		HashMap<String, PlayerItemModel> playerItemModelMap = itemChangeProcData.getPlayerItemMap();
		if (playerItemModelMap == null) {
			param.setResult(ErrorCode.INVALID_PARAM);
			return;
		}

		// 上一個動作沒有成功就不給做
		if (param.getResult() != ErrorCode.SUCCESS) {
			return;
		}

		// 取得寄送禮物的道具清單
		HashMap<String, PlayerItemModel> sendGiftItemMap = itemChangeProcData.getSendGiftItemMap();
		if (param.getChangeWay().isSendGiftRecvItem()) {
			if (sendGiftItemMap == null) {
				param.setResult(ErrorCode.INVALID_PARAM);
				return;
			}
		}
		
		
		int playerLevel = param.getPlayerLevel();
		int lastLevelBoxUseLevel = param.getLastLevelBoxUseLevel();
		
		String gameType = param.getGameHallId();
		String hallType = param.getGameHallName();
		
		// 準備更新 db
		Session session = HibernateManager.txBegin();

		// 處理所有的道具
		ItemChangeWay changeWay = param.getChangeWay();
		ChangeItemLister itemLister = param.getChangeItemLister();
		Iterator<Map.Entry<String, ChangeItem>> it = itemLister.getListIterator();
		ChangeItem itemInfo = itemLister.getNextItem(it);
		while (itemInfo != null) {
			// 取得道具設定資料
			ItemBasic itemBin = ItemInfoManager.getInstance().getItem(itemInfo.getItemId());
			
			if(itemBin.isCrystalCard()) { 
				itemInfo = itemLister.getNextItem(it);
				continue;
			}
			
			
			// 找到玩家身上對應的道具資料 model
			PlayerItemModel playerItemModel = null;

			// 對應此次異動的 log model
			PlayerItemChangeLogModel log = null;
			
			if (changeWay.isAdd()) {
				// 增加 用 id 找
				if (itemBin.isStackable() && !itemBin.isCrystalCard()) {
					for (Map.Entry<String, PlayerItemModel> entry : playerItemModelMap.entrySet()) {
						PlayerItemModel itemModel = entry.getValue();
						if (itemModel.getItemId().equals(itemInfo.getItemId())) {
							playerItemModel = itemModel;
							break;
						}
					}
				}
			} else {
				// 減少 用序號找
				playerItemModel = playerItemModelMap.get(itemInfo.getItemSn());
			}

			// 角色身上的數量
			int currentValue = 0;
			if (playerItemModel != null) {
				currentValue = playerItemModel.getCount();
				log = param.addLog(playerItemModel.getSn(), playerItemModel.getItemId(),
						playerItemModel.getCount(), itemInfo.getChangeCount(), 0, null, null, playerItemModel.getRefineLevel());
			}

			// 變更玩家身上道具 model 資料
			boolean haveNewItem = false;
			if (changeWay.isAdd()) { // 增加
				if (changeWay.isSendGiftRecvItem()) { // 是因為寄送禮物而獲得道具
					PlayerItemModel sendItemModel = sendGiftItemMap.get(itemInfo.getItemSn());

					if (itemBin.isStackable()) { // 可堆疊
						currentValue += itemInfo.getChangeCount();
						if (playerItemModel == null) {
							// 身上沒有就建一個新的
							playerItemModel = ItemDAO.createItemModel
									(
											changeWay,
											param.getPlayerId().toString(),
											sendItemModel.getItemId(),
											sendItemModel.getCount(),
											sendItemModel.getRefineLevel(),
											playerLevel,
											lastLevelBoxUseLevel,
											
											//從寄送的物品獲得該物品來源(其實可堆疊物品的沒甚麼意義，但是為了邏輯正確還是這樣寫)
											sendItemModel.getCreateGameType(),
											sendItemModel.getCreateHallType()
									);
							
							playerItemModel.setSn(sendItemModel.getSn());

							log = param.addLog(playerItemModel.getSn(), playerItemModel.getItemId(), 0,
									sendItemModel.getCount(), playerItemModel.getCount(), null, null, playerItemModel.getRefineLevel());
							log.setOrderId(sendItemModel.getSendGiftOrderId());

							haveNewItem = true;
						} else {
							// 身上有就把數量加到身上的道具中
							playerItemModel.setCount(currentValue);
							log.setAfterChange(currentValue);
							log.setFromItemSn(sendItemModel.getSn());
							log.setOrderId(sendItemModel.getSendGiftOrderId());

							// 也順便更新到新增道具中
							Item newItem = param.getNewItemMap().get(playerItemModel.getSn());
							if (newItem != null) {
								newItem.setCount(currentValue);
							}
						}
						session.saveOrUpdate(playerItemModel);
					} else { // 不可堆疊
						// 直接給成新物品
						
						if(changeWay == ItemChangeWay.ADD_SEND_GIFT){
							//如果是因為虛寶交易取得道具，要把精煉時間改成現在時間，讓他無法精煉
							sendItemModel.setRefineEndTime(TimeSys.currentTimeMills());
						}
						
						PlayerItemModel itemModel = ItemDAO.createItemModel
								(
										changeWay,
										param.getPlayerId().toString(),
										sendItemModel.getItemId(),
										sendItemModel.getCount(),
										sendItemModel.getRefineLevel(),
										playerLevel,
										lastLevelBoxUseLevel,
										
										//從寄送的物品獲得該物品來源
										sendItemModel.getCreateGameType(),
										sendItemModel.getCreateHallType()
								);
						
						itemModel.setSn(sendItemModel.getSn());
						session.saveOrUpdate(itemModel);
						
						log = param.addLog(itemModel.getSn(), itemModel.getItemId(), 0, sendItemModel.getCount(),
								itemModel.getCount(), null, null,itemModel.getRefineLevel());
						log.setOrderId(sendItemModel.getSendGiftOrderId());

						// 有新增資料
						Item newItem = new Item(itemModel);
						param.addNewItem(newItem.getSn(), newItem);
					}
				} else { // 一般獲得道具
					if (itemBin.isStackable()) { // 可堆疊
					
						currentValue += itemInfo.getChangeCount();
						if (playerItemModel == null) {
							playerItemModel = ItemDAO.createItemModel
									(
											changeWay,
											param.getPlayerId().toString(),
											itemInfo.getItemId(),
											currentValue,
											0,
											playerLevel,
											lastLevelBoxUseLevel,
											gameType,
											hallType
									);

							log = param.addLog(playerItemModel.getSn(), playerItemModel.getItemId(), 0, playerItemModel.getCount(), 0, null, null,playerItemModel.getRefineLevel());
							haveNewItem = true;
						}
						playerItemModel.setCount(currentValue);
						session.saveOrUpdate(playerItemModel);
						
						log.setAfterChange(playerItemModel.getCount());
						

					} else { // 不可堆疊
						// 照數量一個一個給
						for (int i = 0; i < itemInfo.getChangeCount(); i++) {
							// 直接給成新物品
							PlayerItemModel itemModel = ItemDAO.createItemModel
									(
											changeWay,
											param.getPlayerId().toString(),
											itemInfo.getItemId(),
											1,
											itemInfo.getItemRefineLevel(),
											playerLevel,
											lastLevelBoxUseLevel,
											gameType,
											hallType
									);
							
							session.saveOrUpdate(itemModel);

							log = param.addLog(itemModel.getSn(), itemModel.getItemId(), 0, 1, 1, null, null,itemModel.getRefineLevel());

							// 有新增資料
							Item newItem = new Item(itemModel);
							param.addNewItem(newItem.getSn(), newItem);
						}
					}
				}
			} else { // 扣
				currentValue -= itemInfo.getChangeCount();
				playerItemModel.setCount(currentValue);
				if (currentValue <= 0) {
					playerItemModel.setStatus(ItemStatus.COUNT_ZERO.getId());
				}
				session.saveOrUpdate(playerItemModel);
				log.setAfterChange(playerItemModel.getCount());
			}

			// 處理寄送禮物將東西拆出來的部份
			if (changeWay == ItemChangeWay.SUB_SEND_GIFT) {
				// 產生寄出物品資料
				PlayerItemModel sendItemModel = ItemDAO.createItemModel
						(
								changeWay,
								null,
								playerItemModel.getItemId(),
								itemInfo.getChangeCount(),
								playerItemModel.getRefineLevel(),
								playerLevel,
								lastLevelBoxUseLevel,
								
								//從原物品獲得物品來源
								playerItemModel.getCreateGameType(),
								playerItemModel.getCreateHallType()
						);

				// 還有剩，拆分成新的一堆
				if (currentValue > 0) {
					log.setToItemSn(sendItemModel.getSn());
				}

				// 全部寄出, 等於整個是搬移
				if (currentValue <= 0) {
					// 序號跟著轉移過去
					sendItemModel.setSn(playerItemModel.getSn());
				}

				sendItemModel.setSendGiftOrderId(param.getOrderId().toString());
				sendItemModel.setStatus(ItemStatus.SEND_GIFT.getId());
				session.saveOrUpdate(sendItemModel);

				// 產生寄出的道具序號清單
				Item itemData = new Item(sendItemModel);
				param.addSendGiftItem(itemData);
			}

			if (itemBin.isStackable()) { // 可堆疊
				// 放入回傳值中, 如果有增加一筆新道具資料就不更新這裡, 放在新增清單中
				if (!haveNewItem) {
					// 沒有新增資料
					itemInfo.setNewCount(currentValue);

					// 將異動資料的序號寫入，回到原 module 時可以寫回角色身上
					itemInfo.setItemSn(playerItemModel.getSn());
				} else {
					playerItemModelMap.put(playerItemModel.getSn(), playerItemModel);

					// 有新增資料
					Item newItem = new Item(playerItemModel);
					param.addNewItem(newItem.getSn(), newItem);
				}
			}
			
			// 取得下一個
			itemInfo = itemLister.getNextItem(it);
		}

		if (param.isAddCrystal()) {// 如果有水晶
			// 直接更新到DB
			Query query = session.createQuery("Update PlayerModel set crystal =:COUNT Where guid =:GUID");
			query.setParameter("GUID", param.getPlayerId().toString());
			query.setParameter("COUNT", param.getCrystalAfterChange());
			query.executeUpdate();
			//創建log
			param.addCrystalLog();
			
			updateAttributeList.addUpdateValue(PlayerAttributeType.CRYSTAL, param.getCrystalAfterChange());
		}

		// 結束更新 db
		boolean res = HibernateManager.txCommitFailRollbackClearSession();
		// db 寫入失敗
		if (res == false) {
			param.setResult(ErrorCode.DB_OPERATION_FAIL);
			return;
		}

		// 成功
		param.setResult(ErrorCode.SUCCESS);
	}
	
	/**
	 * 取得所有的道具Model 
	 * (除了可以設定的 -> additionalSetting = false)
	 * explain select * from itembox where defaultItem = false;
	 * "id"	"select_type"	"table"		"type"	"possible_keys"	"key"	"key_len"	"ref"	"rows"	"Extra"
	 *  "1"	"SIMPLE"		"itembox"	"ALL"	"defaultItem"	\N		\N			\N		"98"	"Using where"
	 * @return
	 */
	public static ArrayList<ItemBoxModel> getAllItems(){
		Session session = HibernateManager.getSession();
		SQLQuery query = session.createSQLQuery("select * from " + ItemBoxModel.TABLENAME  + " where defaultItem = false");
		query.addEntity(ItemBoxModel.class);
		ArrayList<ItemBoxModel>items = (ArrayList<ItemBoxModel>) query.list();
		HibernateManager.endSession();
		return items;
	}
	
	
	/**
	 * 取得所有共用背包內容
	 * explain select * from account_items p where p.ownerAccountId = '0010240c57ebbc4f60d6258d' and p.status = 'NORMAL';
	 * "id"	"select_type"	"table"	"type"	"possible_keys"	"key"	"key_len"	"ref"	"rows"	"Extra"
	 * "1"	"SIMPLE"		"p"		"ref"	"ownerAccountId,status"	"status"	"4"	"const"	"85"	"Using where" 
	 * @param accountId
	 * @return
	 */
	public static ArrayList<AccountItemModel> getAccountItemList(String accountId){
		Session session = HibernateManager.getSession();
		SQLQuery query = session.createSQLQuery("select * from "+ AccountItemModel.TABLENAME + " where ownerAccountId = :ACCOUNT and status = :STATUS");
		query.addEntity(AccountItemModel.class);
		query.setParameter("ACCOUNT", accountId);
		query.setParameter("STATUS", ItemStatus.NORMAL.getId());
		ArrayList<AccountItemModel> accountItems = (ArrayList<AccountItemModel>)query.list();
		HibernateManager.endSession();
		return accountItems;
	}
	
	public static AccountItemChangeLogModel makeAccountItemChangeLogModel(String orderId, String accountId , String playerId, 
			String playerName, int userType, String itemSn, String itemId, 
			String itemName, int changeCount, int beforeCount, int afterCount, 
			AccountItemChangeWay changeWay,String gameHallName, String gameHallId,String openId , String giftTitle,String purchaseId) {
		AccountItemChangeLogModel log = new AccountItemChangeLogModel();
		log.setOrderId(orderId);
		log.setAccountId(accountId);
		log.setPlayerId(playerId);
		log.setPlayerName(playerName);
		log.setUserType(userType);
		log.setItemSn(itemSn);
		log.setItemId(itemId);
		log.setItemName(itemName);


		log.setChangeCount(changeCount);
		log.setBeforeChange(beforeCount);
		log.setAfterChange(afterCount);

		log.setChangeWay(changeWay.getId());
		log.setChangeWayDesc(changeWay);
		
		log.setGameHallName(gameHallName);
		log.setGameHallId(gameHallId);
		
		log.setOpenId(openId);
		log.setGiftTitle(giftTitle);
		log.setPurchaseId(purchaseId);
		
		return log;
	}
	
	/**
	 * explain select * from account_items a Where a.sn = '001f9c1e58faf5f7eea2c57d' and a.ownerAccountId = '0010240c57ebbc4f60d6258d' and a.status = 'NORMAL' for update
	 * "id"	"select_type"	"table"	"type"	"possible_keys"		"key"	"key_len"	"ref"	"rows"	"Extra"
	 * "1"	"SIMPLE"		"a"		"ref"	"sn,ownerAccountId"	"sn"	"768"		"const"	"1"		"Using where"
	 * 把共用背包內的道具狀態改變，有用到 lock for update
	 * @param player
	 * @param accountItemSn
	 * @return
	 */
	public static int changeAccountItemStatus(String accountId, String accountItemSn, ItemStatus from, ItemStatus to) {
		Session session = HibernateManager.txBegin();

		AccountItemModel model = null;
		try{
			SQLQuery query = session.createSQLQuery("select * from " + AccountItemModel.TABLENAME + " Where sn = :SN and ownerAccountId = :ACCOUNT and status = :STATUS for update");
			query.addEntity(AccountItemModel.class);
			query.setParameter("ACCOUNT", accountId);
			query.setParameter("STATUS", from.getId());
			query.setParameter("SN", accountItemSn);
			model = (AccountItemModel) query.uniqueResult();
			
			if(model == null) {
				HibernateManager.endSession();
				return ErrorCode.ITEM_NOT_FOUND_FROM_ACCOUNT_ITEM_LIST;
			}
			
			int count = 0;
			if(to == ItemStatus.NORMAL) {
				//失敗，要把它變成一班狀態
				count = 1;
			}else if(to == ItemStatus.COUNT_ZERO || to == ItemStatus.IS_PICKING || to == ItemStatus.TIME_EXPIRE || to == ItemStatus.SUB_BY_GMTOOL){
				//轉成正在領取，要扣掉
				count = -1;
			}

			model.setCount(model.getCount() + count);
			model.setStatus(to.getId());
			model.setStatusDesc(to);
			
			session.update(model);
			HibernateManager.txCommit();
		}catch (Exception e) {
			HibernateManager.txRollback();
			HibernateManager.endSession();
			if(log.isErrorEnabled()){
				log.error(GameUtility.getCurMethodName(), e);
			}
			return ErrorCode.DB_OPERATION_FAIL;
		}
		
		HibernateManager.endSession();
		return ErrorCode.SUCCESS;
	}
	
	/**
	 * 用SN取得 共用背包的道具資料
	 * explain select * from account_items where sn = '001f9c1e58fafe35eea2c5f7';
	 * "id"	"select_type"	"table"	"type"	"possible_keys"	"key"	"key_len"	"ref"	"rows"	"Extra"
	 *	"1"	"SIMPLE"	"account_items"	"ref"	"sn"	"sn"	"768"	"const"	"1"	"Using where"
	 * @param itemSn
	 * @return
	 */
	public static AccountItemModel getAccountItemModel(String itemSn){
		Session session = HibernateManager.getSession();
		SQLQuery sqlQuery =  session.createSQLQuery("select * from " + AccountItemModel.TABLENAME + " where sn = :SN");
		sqlQuery.addEntity(AccountItemModel.class);
		sqlQuery.setParameter("SN", itemSn);
		AccountItemModel model = (AccountItemModel) sqlQuery.uniqueResult();
		HibernateManager.endSession();
		return model;
	}
	
	/**
	 * 用Item Id 取得 ItemBoxModel
	 * 
	 * explain select * from  itembox where itemId = 'GOLD_COIN_5000';
	 * "id"	"select_type"	"table"	"type"	"possible_keys"	"key"		"key_len"	"ref"	"rows"	"Extra"
	 *	"1"	"SIMPLE"	"itembox"	"const"	"PRIMARY"		"PRIMARY"	"767"		"const"	"1"		""
	 * @param itemId
	 * @return
	 */
	public static ItemBoxModel getItemBoxModelByItemId(String itemId) {
		Session session = HibernateManager.getSession();
		SQLQuery sqlQuery = session.createSQLQuery("select * from " + ItemBoxModel.TABLENAME + " where itemId =:ID");
		sqlQuery.setParameter("ID", itemId);
		sqlQuery.addEntity(ItemBoxModel.class);
		ItemBoxModel model = (ItemBoxModel)sqlQuery.uniqueResult();
		HibernateManager.txCommitFailRollbackClearSession();
		return model;
	}
	
	
	/**
	 * 確認所有禮包是否有這個道具
	 * 	explain select itemId from itembox where classId = 4 and randomItemList like '%GOLD_COIN_5000%';
	 * "id"		"select_type"	"table"		"type"	"possible_keys"		"key"		"key_len"	"ref"		"rows"	"Extra"
	 *	"1"		"SIMPLE"		"itembox"	"ref"	"classId"			"classId"	"4"			"const"		"8"		"Using where"
	 * @param itemId
	 * @return
	 */
	public static boolean isItemInBonusPack(String itemId){
		Session session = HibernateManager.getSession();
		SQLQuery sqlQuery = session.createSQLQuery("select itemId from " + ItemBoxModel.TABLENAME + " where classId = 4 and randomItemList like :ID");
		sqlQuery.setParameter("ID", "%"+itemId+"%");		
		sqlQuery.addScalar("itemId", StandardBasicTypes.STRING);
		ArrayList<String> itemIds = (ArrayList<String>)sqlQuery.list();
		HibernateManager.endSession();
		
		if (itemIds.size() > 0) {
			return true;
		}
		else {
			return false;
		}
	}
	
	public static final String REDIS_KEY_ITEM_BOX_UPDATE ="ITEM_BOX_UPDATE";
	
	/**
	 * GMTool說有道具的更新時，
	 * 把組成的道具轉成JSON ， 存放在Reids 的 bonus pack DB中，五分鐘後自動刪除
	 * 讓各個Common可以讀出來用
	 * @param itemBox
	 */
	public static void saveItemBoxToRedis(String itemBoxJson, String subKey){
		Jedis jedis =  getJedis();
		String key = REDIS_KEY_ITEM_BOX_UPDATE + subKey;
		jedis.set(key, itemBoxJson);
		jedis.expire(key, (int)TimeConstants.SECOND_PRE_MINUTE);
		returnJedis(jedis);
	}
	
	/**
	 * GMTool說有道具的更新時，
	 * Common 用此方把Json讀出來再轉成  Object 
	 * 修改暫存
	 * @return
	 */
	public static String readItemBoxJsonFromRedis(String subKey){
		Jedis jedis =  getJedis();
		String json = jedis.get(REDIS_KEY_ITEM_BOX_UPDATE + subKey);
		returnJedis(jedis);
		return json;
	}
	
	private static Jedis getJedis(){
		Jedis jedis = JedisManager.getResource(ModuleName.BONUS_PACK);
		return jedis;
	}
	
	private static void returnJedis(Jedis jedis){
		JedisManager.returnResource(ModuleName.BONUS_PACK, jedis);
	}
}
